package com.boglond.util;

import com.boglond.builder.GenerateXMLInterfaceImpl;
import com.boglond.entity.ColumnInfoDO;
import com.boglond.entity.ErrorCodeDO;
import com.boglond.entity.TableInfoDO;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class FreemarkerUtil {
    private static final Logger log = LoggerFactory.getLogger(FreemarkerUtil.class);

    private static final String TEMPLATE_PATH = "code-printer\\src\\main\\java\\com\\boglond\\template";
    private static final String PRJECT_SAVE_PATH = PropertyUtil.getValueByKey("project.save.path");
    private static final String PROJECT_FLAG = PropertyUtil.
            getValueByKey("project.flag");
    private static final String APP_NAME = PropertyUtil.getValueByKey("app.name");
    private static final String TABLE_NAME_CN = PropertyUtil.getValueByKey("unified.table.name.cn");
    private static final String ERROR_CODE_BASE_NUM = PropertyUtil.
            getValueByKey("errorCode.baseNumber");
    private static final String DAO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("dao.class.save.path")).toString();
    private static final String PO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("po.class.save.path")).toString();
    private static final String REPOSITORY_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("repository.class.save.path")).toString();
    private static final String REPOSITORY_IMPL_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("repositoryImpl.class.save.path")).toString();
    private static final String BASIC_SERVICE_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("basicService.class.save.path")).toString();
    private static final String BASIC_SERVICE_IMPL_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("basicServiceImpl.class.save.path")).toString();
    private static final String BUSINESS_SERVICE_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("businessService.class.save.path")).toString();
    private static final String BUSINESS_SERVICE_IMPL_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("businessServiceImpl.class.save.path")).toString();
    private static final String REST_SERVICE_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restService.class.save.path")).toString();
    private static final String REST_SERVICE_IMPL_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restServiceImpl.class.save.path")).toString();
    private static final String SERVICE_ADD_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceAddReqDTO.class.save.path")).toString();
    private static final String SERVICE_UPD_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceUpdReqDTO.class.save.path")).toString();
    private static final String SERVICE_QRY_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceQryReqDTO.class.save.path")).toString();
    private static final String SERVICE_DEL_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceDelReqDTO.class.save.path")).toString();
    private static final String SERVICE_ADD_RESP_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceAddRespDTO.class.save.path")).toString();
    private static final String SERVICE_QRY_RESP_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceQryRespDTO.class.save.path")).toString();

    private static final String REST_ADD_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restAddReqDTO.class.save.path")).toString();
    private static final String REST_DEL_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restDelReqDTO.class.save.path")).toString();
    private static final String REST_UPD_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restUpdReqDTO.class.save.path")).toString();
    private static final String REST_QRY_REQ_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restQryReqDTO.class.save.path")).toString();
    private static final String REST_ADD_RESP_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restAddRespDTO.class.save.path")).toString();
    private static final String REST_QRY_RESP_DTO_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("restQryRespDTO.class.save.path")).toString();
    private static final String ERROR_CODE_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("errorCode.class.save.path")).toString();
    private static final String SERVICE_EXCEPTION_CLASS_SAVE_PATH = new StringBuffer().append(PRJECT_SAVE_PATH).
            append(PropertyUtil.getValueByKey("serviceException.class.save.path")).toString();
    public static void createMapperClass(String entityName){
        generateClassFile(entityName,null,
                "mapper.ftl",
                DAO_CLASS_SAVE_PATH,
                "Mapper.java",
                "{} Mapper类 创建成功 !",null);
    }

    public static void createPOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "po.ftl",
                PO_CLASS_SAVE_PATH,
                "PO.java",
                "{} PO类 创建成功 !",null);
    }

    public static void createRepositoryClass(String entityName){
        generateClassFile(entityName,null,
                "repository.ftl",
                REPOSITORY_CLASS_SAVE_PATH,
                "Repository.java",
                "{} Repository类 创建成功 !",null);
    }

    public static void createRepositoryImplClass(String entityName,
                                                 String upperTableName,
                                                 String tableName){
        generateClassFile4RepositoryImpl(entityName,
                "repository_impl.ftl",
                REPOSITORY_IMPL_CLASS_SAVE_PATH,
                "RepositoryImpl.java",
                "{} Repository类 创建成功 !",
                upperTableName,tableName);
    }


    public static void createBasicServiceClass(String entityName){
        generateClassFile(entityName,null,
                "basic_service.ftl",
                BASIC_SERVICE_CLASS_SAVE_PATH,
                "BasicAPI.java",
                "{} 基础service API类 创建成功 !",null);
    }

    public static void createBasicServiceImplClass(String entityName){
        generateClassFile(entityName,null,
                "basic_service_impl.ftl",
                BASIC_SERVICE_IMPL_CLASS_SAVE_PATH,
                "BasicService.java",
                "{} 基础service 实现类 创建成功 !",null);
    }

    public static void createRestServiceClass(String entityName,String url){
        generateClassFile(entityName,null,
                "rest_service.ftl",
                REST_SERVICE_CLASS_SAVE_PATH,
                "RestAPI.java",
                "{} rest service API类 创建成功 !",url);
    }

    public static void createRestServiceImplClass(String entityName, String upperTableName){
        generateClassFile(entityName,null,
                "rest_service_impl.ftl",
                REST_SERVICE_IMPL_CLASS_SAVE_PATH,
                "RestService.java",
                "{} rest service 实现类 创建成功 !",null, upperTableName);
    }

    public static void createServiceQryReqDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "service_req_dto.ftl",
                SERVICE_QRY_REQ_DTO_CLASS_SAVE_PATH,
                "ReqDTO.java",
                "{} service qry req dto 类 创建成功!",null);
    }

    public static void createServiceDelReqDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "service_id_req_dto.ftl",
                SERVICE_DEL_REQ_DTO_CLASS_SAVE_PATH,
                "IdReqDTO.java",
                "{} service del req dto 类 创建成功!",null);
    }

    public static void createServiceAddRespDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "service_id_resp_dto.ftl",
                SERVICE_ADD_RESP_DTO_CLASS_SAVE_PATH,
                "IdRespDTO.java",
                "{} service add resp dto 类 创建成功!",null);
    }

    public static void createServiceQryRespDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "service_resp_dto.ftl",
                SERVICE_QRY_RESP_DTO_CLASS_SAVE_PATH,
                "RespDTO.java",
                "{} service qry resp dto 类 创建成功!",null);
    }

    public static void createRestAddReqDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "rest_add_req_dto.ftl",
                REST_ADD_REQ_DTO_CLASS_SAVE_PATH,
                "AddRestReqDTO.java",
                "{} rest add req dto 类 创建成功!",null);
    }

    public static void createRestDelReqDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "rest_id_req_dto.ftl",
                REST_DEL_REQ_DTO_CLASS_SAVE_PATH,
                "IdRestReqDTO.java",
                "{} rest del req dto 类 创建成功!",null);
    }

    public static void createRestQryReqDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "rest_req_dto.ftl",
                REST_QRY_REQ_DTO_CLASS_SAVE_PATH,
                "RestReqDTO.java",
                "{} rest qry req dto 类 创建成功!",null);
    }

    public static void createRestAddRespDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "rest_id_resp_dto.ftl",
                REST_ADD_RESP_DTO_CLASS_SAVE_PATH,
                "IdRestRespDTO.java",
                "{} rest add resp dto 类 创建成功!",null);
    }

    public static void createRestQryRespDTOClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "rest_resp_dto.ftl",
                REST_QRY_RESP_DTO_CLASS_SAVE_PATH,
                "RestRespDTO.java",
                "{} rest qry resp dto 类 创建成功!",null);
    }

    public static void createErrorCodeClass(List<TableInfoDO> tableInfoDOList){
        generateErrorCodeClassFile("ErrorCode",
                tableInfoDOList,
                "error_code.ftl",
                ERROR_CODE_CLASS_SAVE_PATH,
                ".java",
                "{} ErrorCode 枚举类 创建成功!");
    }

    public static void createServiceExceptionClass(String entityName, List<ColumnInfoDO> columnsInfo){
        generateClassFile(entityName,columnsInfo,
                "service_exception.ftl",
                SERVICE_EXCEPTION_CLASS_SAVE_PATH,
                "ServiceException.java",
                "{} ServiceException 类 创建成功!",null);
    }

    public static void generateErrorCodeClassFile(String entityName,
                                            List<TableInfoDO> tableInfoDOList,
                                            String templateName,
                                            String classSavePath,
                                            String fileNameSuffix,
                                            String logContent){
        Writer out = null;
        try {
            Map<String, Object> dataMap =
                    buildErrorCodeData4Freemarker(tableInfoDOList);
            Template template = buildTemplateEngine(templateName);
            out = buildFileOutputStream(entityName,classSavePath,fileNameSuffix);
            template.process(dataMap, out);
            log.info(logContent,entityName);
        } catch (Exception e) {
            log.error("生成类文件异常，详细信息为:{}", e);
        } finally {
            flushStream(out);
        }
    }

    /**
     * 根据参数生成类文件
     * @param entityName 实体名称
     * @param columnsInfo 数据库字段信息
     * @param templateName 类模板名
     * @param classSavePath 类保存路径
     * @param fileNameSuffix 类文件名后缀
     * @param logContent 日志内容
     * @param url 接口路径
     */
    private static void generateClassFile(String entityName,
                                          List<ColumnInfoDO> columnsInfo,
                                          String templateName,
                                          String classSavePath,
                                          String fileNameSuffix,
                                          String logContent,
                                          String url){
        Writer out = null;
        try {
            Map<String, Object> dataMap =
                    buildData4Freemarker(entityName, columnsInfo, null, null, url);
            Template template = buildTemplateEngine(templateName);
            out = buildFileOutputStream(entityName,classSavePath,fileNameSuffix);
            template.process(dataMap, out);
            log.info(logContent,entityName);
        } catch (Exception e) {
            log.error("生成类文件异常，详细信息为:{}", e);
        } finally {
            flushStream(out);
        }
    }

    private static void generateClassFile(String entityName,
                                          List<ColumnInfoDO> columnsInfo,
                                          String templateName,
                                          String classSavePath,
                                          String fileNameSuffix,
                                          String logContent,
                                          String url,
                                          String upperTableName){
        Writer out = null;
        try {
            Map<String, Object> dataMap =
                    buildData4Freemarker(entityName, columnsInfo, null, upperTableName, url);
            Template template = buildTemplateEngine(templateName);
            out = buildFileOutputStream(entityName,classSavePath,fileNameSuffix);
            template.process(dataMap, out);
            log.info(logContent,entityName);
        } catch (Exception e) {
            log.error("生成类文件异常，详细信息为:{}", e);
        } finally {
            flushStream(out);
        }
    }

    /**
     * 生成资源库类文件
     * @param entityName 实体名称
     * @param templateName 类模板名
     * @param classSavePath 类保存路径
     * @param fileNameSuffix 类文件名后缀
     * @param logContent 日志内容e
     * @param upperTableName 转成大写的表名
     * @param tableName 未经处理的表名
     */
    public static void generateClassFile4RepositoryImpl(String entityName,
                                                        String templateName,
                                                        String classSavePath,
                                                        String fileNameSuffix,
                                                        String logContent,
                                                        String upperTableName,
                                                        String tableName){
        Writer out = null;
        try {
            Map<String, Object> dataMap =
                    buildData4Freemarker(entityName, null, tableName, upperTableName, null);
            out = buildFileOutputStream(entityName,classSavePath,fileNameSuffix);
            Template template = buildTemplateEngine(templateName);
            template.process(dataMap, out);
            log.info(logContent,entityName);
        } catch (Exception e) {
            log.error("生成类文件异常，详细信息为:{}", e);
        } finally {
            flushStream(out);
        }
    }
    private static Map<String, Object> buildData4Freemarker(String entityName,
                                             List<ColumnInfoDO> columnsInfo,
                                             String tableName,
                                             String upperTableName,
                                             String url){
        Map<String, Object> dataMap = new HashMap<>();

        if(null != columnsInfo && columnsInfo.size() > 0){
            dataMap.put("columnInfos", columnsInfo);
        }

        if(null != url && !"".equals(url.trim())){
            dataMap.put("url", url);
        }

        if(null != tableName && !"".equals(tableName.trim())){
            dataMap.put("tableName", tableName);
        }

        if(null != upperTableName && !"".equals(upperTableName.trim())){
            dataMap.put("upperTableName", upperTableName);
        }

        dataMap.put("entityName", entityName);
        dataMap.put("appName", APP_NAME);
        dataMap.put("projectFlag", PROJECT_FLAG);
        dataMap.put("entityParamName",entityName.substring(0,1).toLowerCase()
                + entityName.substring(1,entityName.length()));
        dataMap.put("entityCN", TABLE_NAME_CN);

        return dataMap;

    }

    private static Map<String, Object> buildErrorCodeData4Freemarker(
            List<TableInfoDO> tableInfoDOList){
        Map<String, Object> dataMap = new HashMap<>();

        List<ErrorCodeDO> constsNameOfTable = new ArrayList<>();
        ErrorCodeDO errorCodeDO = null;
        int errorCodeBaseNum = Integer.valueOf(ERROR_CODE_BASE_NUM);
        int size = tableInfoDOList.size();
        for(int i = 0; i < size; i++){
            errorCodeDO = new ErrorCodeDO();
            errorCodeDO.setTableName(tableInfoDOList.get(i).getUpperTableName());
            errorCodeDO.setCodeValue4Qry(String.valueOf(errorCodeBaseNum + 4 * i));
            errorCodeDO.setCodeValue4Save(String.valueOf(errorCodeBaseNum + 1 + 4 * i));
            errorCodeDO.setCodeValue4Del(String.valueOf(errorCodeBaseNum + 2 + 4 * i));
            errorCodeDO.setCodeValue4Count(String.valueOf(errorCodeBaseNum + 3 + 4 * i));
            errorCodeDO.setCodeValue4ID(String.valueOf(errorCodeBaseNum + 4 + 4 * i));
            constsNameOfTable.add(errorCodeDO);
        }
        dataMap.put("constsNameList", constsNameOfTable);
        dataMap.put("projectFlag", PROJECT_FLAG);

        return dataMap;

    }
    private static Writer buildFileOutputStream(String entityName,
                                         String classSavePath,
                                         String fileNameSuffix) throws Exception{
        File filePath = new File(classSavePath);
        if(!filePath.exists()){
            filePath.mkdirs();
        }
        String fileName = entityName + fileNameSuffix;
        File docFile = new File(filePath,fileName);
        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
        return out;
    }
    private static Template buildTemplateEngine(String templateName) throws Exception {
        // step1 创建freeMarker配置实例
        Configuration configuration = new Configuration();
        // step2 获取模版路径
        configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));
        // step3 加载模版文件
        Template template = configuration.getTemplate(templateName);

        return template;

    }
    private static void flushStream(Writer out){
        try {
            if (null != out) {
                out.flush();
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }


}
